package com.zhugang.week03;

import java.util.Stack;

/**
 * @program algorithms
 * @description: stackCalculate
 * @author: chanzhugang
 * @create: 2022/06/05 10:28
 */
public class StackCalculate2 {

    /**
     * 面试题16.26 计算器
     *
     * @param s
     * @return
     */
    public int calculate(String s) {
        /**
         * 数字栈存数字，运算符栈存运算符，
         * 运算符栈顶元素和当前字符比较（当前字符优先级小），取出数字栈两个数字和运算符栈栈顶元素计算
         */
        Stack<Integer> nums = new Stack<>();
        Stack<Character> ops = new Stack<>();
        int i = 0;
        int n = s.length();
        while (i < n) {
            char c = s.charAt(i);
            if (c == ' ') {
                i++;
            } else if (isDigit(c)) {
                int number = 0;
                while (i < n && isDigit(s.charAt(i))) {
                    number = number * 10 + (s.charAt(i) - '0');
                    i++;
                }
                nums.push(number);
            } else {
                // 进一步优化
                while (!ops.isEmpty() && !piror(c, ops.peek())) {
                    fetchAndCal(nums, ops);
                }
                ops.push(c);
                i++;
            }
        }
        while (!ops.isEmpty()) {
            fetchAndCal(nums, ops);
        }
        return nums.pop();
    }

    private void fetchAndCal(Stack<Integer> nums, Stack<Character> ops) {
        Integer num2 = nums.pop();
        Integer num1 = nums.pop();
        Character op = ops.pop();
        int res = cal(op, num1, num2);
        nums.push(res);
    }

    private int cal(Character op, Integer num1, Integer num2) {
        if (op == '+') {
            return num1 + num2;
        } else if (op == '-') {
            return num1 - num2;
        } else if (op == '*') {
            return num1 * num2;
        } else if (op == '/') {
            return num1 / num2;
        }
        return -1;
    }

    private boolean piror(char a, Character b) {
        return (a == '*' || a == '/') && (b == '+' || b == '-');
    }

    private boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }
}